#include "ib_virtual_app.hh"
#include "ib_m.h"

using namespace omnetpp;
using namespace ai_sim;

Define_Module(IBVirtualApp);

void IBVirtualApp::initialize()
{}

void IBVirtualApp::handleMessage(cMessage* msg)
{
    const char* g = msg->getArrivalGate()->getFullName();
    EV << "-I- " << getFullPath() << " received sent msg from:" 
        << g << omnetpp::endl;
    // incoming FlowMsg from virtual port
    // forward to GPU
    if (dynamic_cast<VirtualFlowMsg*>(msg))
    {
        send(reinterpret_cast<FlowMsg*>(msg), "gpu_port$o");
    }
    // incoming FlowMsg from GPU
    // convert to IBAppMsg; push to queue
    else if (dynamic_cast<FlowMsg*>(msg))
    {
        AppMsgQueue.push(reinterpret_cast<FlowMsg*>(msg));
        trySendNext();
    }
    // received a whole message
    else if (msg->getKind() == IB_DONE_MSG)
    {
        delete msg;
    }
    // finished sending a whole message
    // send the FlowMsg to dest app via virtual switch
    else if (msg->getKind() == IB_SENT_MSG)
    {
        delete msg;
        EV<<"Received SENT MSG IN IB APP FROM IB GEN!!"<<omnetpp::endl;
        send(msg_being_sent_, "virtual_data$o");
        msg_being_sent_ = nullptr;
        trySendNext();
    }
}

IBAppMsg* IBVirtualApp::toIBAppMsg(const FlowMsg* flow_m) const 
{
    IBAppMsg* p_msg = new IBAppMsg(nullptr, IB_APP_MSG);
    const Flow& flow = flow_m->getThe_flow();
    unsigned int lenBytes;
    if ((unsigned int)flow.data_size % msgMtuLen_B_)
        lenBytes = (flow.data_size / msgMtuLen_B_ + 1) * msgMtuLen_B_;
    else 
        lenBytes = flow.data_size;
    p_msg->setAppIdx(flow.flowId);
    p_msg->setMsgIdx(flow.flowId);
    p_msg->setDstLid(flow.destIdx);
    p_msg->setSQ(0);
    p_msg->setLenBytes(lenBytes);
    p_msg->setLenPkts(lenBytes / msgMtuLen_B_);
    p_msg->setMtuBytes(msgMtuLen_B_);
    delete flow_m;
    return p_msg;
}

VirtualFlowMsg* IBVirtualApp::toVirtualFlowMsg(FlowMsg* flow_m)
{
    VirtualFlowMsg *vf_msg = new VirtualFlowMsg();
    vf_msg->setKind(DATA_MSG);
    vf_msg->setThe_flow(flow_m->getThe_flow());
    return vf_msg;
}

void IBVirtualApp::trySendNext()
{
    if (msg_being_sent_ || AppMsgQueue.empty())
        return;
    FlowMsg* msg = AppMsgQueue.front();
    AppMsgQueue.pop();
    msg_being_sent_ = toVirtualFlowMsg(msg);
    send(toIBAppMsg(msg), "out$o");
    EV<<"SUCESSFULLY SEND OUT IN IB VIR APP!"<<omnetpp::endl;
}

IBVirtualApp::~IBVirtualApp()
{
	if(msg_being_sent_)
		delete msg_being_sent_;
     while(!AppMsgQueue.empty())
	{
		FlowMsg* msg = AppMsgQueue.front();
		delete msg;
	}
}

void IBVirtualApp::finish()
{
	
}
